// 回复正文解析


import {AnonymousUtils} from "../utils/AnonymousUtils.ts";
import {decode} from "../utils/StrUtils.ts";

/**
 * 回复正文节点
 * @property type 节点类型
 * @property code 代码原文
 * @property params 参数
 * @property text 文本
 * @property children 子节点
 * @property inCollapse 是否在折叠中, 用于 dice计算
 */
export interface BbsCodeNode {
    type: string
    code: string
    params?: string | undefined
    text?: string | undefined
    children?: BbsCodeNode[] | undefined
    inCollapse?: boolean | undefined
}

// 解析正文为节点
export const parseContent = (content?: string | undefined): BbsCodeNode[] | undefined => {
    if (!content) return undefined
    let c = content
    if (c.includes('[code]')) {
        c = c.replace(/^(.+?)\[code]/g, (text, _) => preHandle(text))
        c = c.replace(/\[\/code](.*?)(?=\[code]|$)/g, (text, _) => preHandle(text))
    } else {
        c = preHandle(c)
    }
    // console.warn('handled', c)

    const nodes = parseCode(c);
    // console.log('content', {content})
    // console.log("nodes", nodes)
    return nodes
}

interface NodeType {
    name: string
    regexp: RegExp
}

const matchRegExp = (type: string) => new RegExp(`^\\[${type}([^[]*?)\\](.*?)\\[\\/${type}\\]`)
const parseRegExp = (type: string) => new RegExp(`^\\[${type}([^[]*?)\\](.*)\\[\\/${type}\\]$`)

const nodeTypes: NodeType[] = [
    {name: "randomblocks", regexp: matchRegExp("randomblocks")},
    {name: "randomblock", regexp: matchRegExp("randomblock")},
    {name: "underline", regexp: matchRegExp("underline")},
    {name: "collapse", regexp: matchRegExp("collapse")},
    {name: "attach", regexp: matchRegExp("attach")},
    {name: "heading", regexp: matchRegExp("heading")},
    {name: "ul", regexp: matchRegExp("ul")},
    {name: "quote", regexp: matchRegExp("quote")},
    {name: "del", regexp: matchRegExp("del")},
    {name: "bold", regexp: matchRegExp("bold")},
    {name: "italic", regexp: matchRegExp("italic")},
    {name: "tr", regexp: matchRegExp("tr")},
    {name: "font", regexp: matchRegExp("font")},
    {name: "color", regexp: matchRegExp("color")},
    {name: "table", regexp: matchRegExp("table")},
    {name: "size", regexp: matchRegExp("size")},
    {name: "right", regexp: matchRegExp("right")},
    {name: "left", regexp: matchRegExp("left")},
    {name: "dice", regexp: matchRegExp("dice")},
    {name: "align", regexp: matchRegExp("align")},
    {name: "pid", regexp: matchRegExp("pid")},
    {name: "flash", regexp: matchRegExp("flash")},
    {name: "img", regexp: matchRegExp("img")},
    {name: "uid", regexp: matchRegExp("uid")},
    {name: "tid", regexp: matchRegExp("tid")},
    {name: "url", regexp: matchRegExp("url")},
    {name: "td", regexp: matchRegExp("td")},
    {name: "code", regexp: matchRegExp("code")},
    {name: "at", regexp: matchRegExp("at")},
    {name: "li", regexp: matchRegExp("li")},
    {name: "text", regexp: matchRegExp("text")},
]

const textNode = (text: string): BbsCodeNode => {
    return {type: "text", text: decode(text), code: ""}
}
const buildBbsCode = (nodeType: string, content: string, inCollapse: boolean = false): BbsCodeNode => {
    const regexp = parseRegExp(nodeType)

    const matcher = regexp.exec(content);
    if (matcher) {
        let params = matcher[1].replace("=", "")
        let innerCode = matcher[2]
        let text
        let children: BbsCodeNode[] | undefined
        // 当节点类型为如下类型星，进行一些特殊处理

        // 如果为URL标签且参数为空，则将中间文本作为参数
        if (nodeType === "url" && !params) {
            params = innerCode
                .replace("https://bbs.nga.cn", "")
                .replace("https://ngabbs.com", "")
                .replace("https://nga.178.com", "")
                .replace("https://g.nga.cn", "")
        }
        // 如果tid和pid参数为空，且中间文本为数字，则将数字作为参数，文本添加前缀
        if (nodeType === "tid" || nodeType === "pid") {
            if (!params && innerCode.match(/^\d+$/)) {
                params = innerCode
                if (nodeType === "tid") {
                    innerCode = `主题 ${innerCode}`
                } else if (nodeType === "pid") {
                    innerCode = `回复 ${innerCode}`
                }
            }
        }
        // 对部分解析尽头的节点做特殊处理，其他的继续解析
        switch (nodeType) {
            case "code": {
                text = decode(innerCode.replace(/<br\/*>/g, "\n"))
                break
            }
            case "flash":
            case "attach":
            case "img":
            case "dice": {
                params = innerCode.trim().replace(/<\/*br>/g, "")
                break
            }
            default: {
                children = parseCode(innerCode, inCollapse || nodeType === "collapse")?.filter(node => {
                    switch (nodeType) {
                        case "table":
                            return node.type === 'tr'
                        case "tr":
                            return node.type === 'td'
                        case "list":
                            return node.type === 'li'
                        default:
                            return true
                    }
                })

                break
            }
        }

        return {
            type: nodeType,
            code: content,
            inCollapse: inCollapse,
            params, children, text
        }
    }
    return {
        type: nodeType,
        code: content,
    }
}

const findNodeType = (c: string): string | undefined => c ? nodeTypes.find((nt) => nt.regexp.exec(c))?.name : undefined

/**
 * 尝试从文本中截取指定类型的标签
 */
const subBbsCode = (text: String, nodeType: string): string | undefined => {
    let index = 0
    // 开始标签
    const startCode = `[${nodeType}`
    // 结束标签
    const endCode = `[/${nodeType}]`

    while (index < text.length) {
        // 从 index 位置往后找结束标签的位置
        const end = text.indexOf(endCode, index) + endCode.length
        // 截取开始到结束标签的位置文本
        const subText = text.substring(0, end)
        // 如果截取的文本中 开始和结束标签的数量相同，则正确匹配
        if (subText.split(startCode).length == subText.split(endCode).length) {
            return subText
        }
        // 匹配失败，如果当前位置小于结束位置，将当前位置设置为结束位置
        if (index < end) {
            index = end
        } else {
            return undefined
        }
    }
    return undefined
}

const parseCode = (content: string | undefined, inCollapse: boolean = false): BbsCodeNode[] => {
    if (!content) return []
    let c = content || ""
    const res: BbsCodeNode[] = []
    let index = 0
    while (index < c.length) {
        // 当前位置非 [ 则直接跳到下一个字符
        if (c[index] != '[') {
            index++;
            continue
        }

        // 当前位置是 [ 时，裁剪前后的代码，对后半代码尝试进行匹配
        const prefix = c.substring(0, index)
        const suffix = c.substring(index)

        // 尝试匹配一个节点类型
        const nodeType = findNodeType(suffix)
        // 如果匹配失败跳到下一个字符
        if (!nodeType) {
            index++;
            continue
        }

        // 匹配成功，表示 [ 后的字符属于可识别的标签名称，但尚未确定它是否正确匹配了结束标签
        // 当前位置大于0 时，将前方代码作为文本添加到返回结果
        if (index > 0) res.push(textNode(prefix))
        // 尝试从后半代码中截取该标签，要求起始和结束标签正确匹配
        const bbsCode = subBbsCode(suffix, nodeType)
        if (!bbsCode) {
            console.warn("标签匹配失败: " + suffix)
            index++;
            continue
        }
        // 生成节点冰添加到返回结果
        res.push(buildBbsCode(nodeType, bbsCode, inCollapse))
        // // 截断原代码段，将index置0
        c = suffix.substring(bbsCode.length)
        index = 0
    }
    // 遍历结束，如果光标位置依然大于0，表示有剩余文本，添加一个文本节点
    if (index > 0) {
        res.push(textNode(c))
    }
    return res
}


// 正文预处理
const preHandle = (content: string): string => content
    // 替换回复模板中的匿名用户
    .replace(/\[uid](#anony_.+?)\[\/uid]/g, (_, $1) => AnonymousUtils.transform($1) || "")
    // 替换不规范的 ==
    .replace(/===(.*?)===/g, "[h]$1[/h]")
    // 为预设回复模板添加 quote
    .replace(/(\[b]Reply to.+?\[\/b])/g, "[quote]$1[/quote]")
    // 规范化改名
    .replace(/\[(\/*)u]/g, "[$1underline]")
    .replace(/\[(\/*)b]/g, "[$1bold]")
    .replace(/\[(\/*)i]/g, "[$1italic]")
    .replace(/\[(\/*)r]/g, "[$1right]")
    .replace(/\[(\/*)l]/g, "[$1left]")
    .replace(/\[(\/*)h]/g, "[$1heading]")
    // 列表相关标签规范化，改名和删除换行
    .replace(/(<br\/*>)*\[\*]/g, "[/li][li]")
    .replace(/(<br\/*>)*\[\/list]/g, "[/li][/ul]")
    .replace(/\[list]/g, "[ul][li]")
    .replace(/\[li]\[\/li]/g, "")
    // 对本身将渲染为一行的标签，删除前面的多余换行
    .replace(/(<br\/*>)+\[(\/*)heading]/g, "[$2heading]")
    .replace(/(<br\/*>)+\[(\/*)list]/g, "[$2list]")
    .replace(/(<br\/*>)+\[(\/*)quote]/g, "[$2quote]")
    .replace(/(<br\/*>)+\[(\/*)randomblock]/g, "[$2randomblock]")
    // 为连续的随机块添加 parent
    .replace(/(\[randomblock].+?\[\/randomblock])+/g, (text) => `[randomblocks]${text}[/randomblocks]`)
    // td 标签规范化
    .replace(/\[td(\d+)]/g, "[td=width$1]")
    .replace(/\[td (.+?)]/g, (_, $1) => `[td=${$1.replace("=", "")}]`)
    // @ 标签规范化
    .replace(/\[@(.+?)]/g, "[at=$1]$1[/at]")